"""
This modules assumes you are compiling SQLObjects. It tries to save you some
hassle by autoadding useful functions, like "by...", "add...", and "remove..."

It's also more intelligent than the default compiler - it doesn't grab all the
property-type objects. For ForeignKeys, MultipleJoins, etc, we make them into
ROPC calls instead.
"""
import os

from pythontojavascript.compilers import default
from pythontojavascript import ObjectSerializer

def compile(obj):
  js = []
  
  parseObject(obj)
  special_cls_methods = generateSpecialClassMethods(obj)
    
  default.makeObjPublishable(obj)
    
  js.append(default.buildObject(obj,special_cls_methods,
            addToCache="PythonObject.sqlobject.addToCache"))
  js.append(default.buildInstanceMethods(obj))
  js.append(default.buildClassMethods(obj))
  return "\n".join(js)

def template():
  # Add special JS just for mimicking SQLObject
  path = os.path.dirname(__file__)
  f = open(path + "/templates/sqlobject.js")
  js = f.read()
  f.close()
  return js

def parseObject(obj):
  """
  For SQLObjects, we don't want to use all the "property" objects because some
  of them are SelectResults, etc. So we have to be a bit smarter.
  """
  ObjectSerializer.addProperty(obj,"id")  # SQLObjects have "id"s
  # Check the columns to get our serialized properties.
  columns = obj.sqlmeta.columns
  for c in columns:
    if checkIfProperty(columns[c]):
      ObjectSerializer.addProperty(obj,c)
    else:
      raise Exception("This hasn't been implemented yet!")
      ObjectSerializer.addInstMethodByName(obj,c)
    if columns[c].alternateID:
      ObjectSerializer.addClassMethodByName(obj,"by"+c,sync=True)

  # We also want to add "add..." and "remove..." functions for Joins
  processJoins(obj)
  
def generateSpecialClassMethods(obj):
  """
  For SQLObject, we want some special "magic"methods:
  -get
  -set
  -select
  ...maybe others?
  """
  ObjectSerializer.addAuthorizedMethod("get")
  ObjectSerializer.addAuthorizedMethod("set")
  ObjectSerializer.addAuthorizedMethod("select")
  js = """%(name)s.get = PythonObject.get
          %(name)s.select = PythonObject.select
          %(name)s.set = PythonObject.set
       """
  obj_name = obj.__name__
  return js % {"name":obj_name}

def checkIfProperty(col):
  """
  If there are columns we don't want to serialize, add them here.
  NOTE: currently we serialize everything. Eventually, SOForeignKey might become
  an instance method that returns an object...
  """
  skip = []
  name = col.columnDef.baseClass.__name__
  if name in skip:
    return False
  return True  
  
def processJoins(obj):
  """
  SQLObject has these cool "join" things. We want to:
  -Provide an ROPC call to all joins. We want to do this synchronously by
   default, and cache the results.
  -For RelatedJoins, provide "add" and "remove" functions like SQLObject does

  We CACHE Joins by default. This is because you want to loop over them...
  """
  joins = obj.sqlmeta.joinDefinitions
  for j in joins:
    name = j.joinMethodName
    ObjectSerializer.addInstMethodByName(obj,name,sync=True,cache=True,
                                          skipcache=False)
    if j.baseClass.__name__ == "SORelatedJoin":
      generateByAndRemoveMethods(obj,j)
  
def generateByAndRemoveMethods(obj,join):
  """
  When we generate the "add" and "remove" functions, we also need to add/remove
  from the Javascript copy of the object.
  TODO(dobromirv): We actually don't need this add/remove part right now,
  since the call is always to the server!!
  WAIT, WHAT ABOUT CACHING???
  """
  remove_guts = """ 
                for(var i=0;i<this.%(name)ss.length;i++) {
                  if(this.%(name)ss[i] == args) {
                    this.%(name)ss.splice(i,1);
                  }
                }"""
  add_guts = """this.%s.push(args)"""
  
  name = join.name
  other_class = join.kw.get("otherClass")
  ObjectSerializer.addInstMethodByName(obj,MethodName="add"+other_class,
                                        sync=True)
  ObjectSerializer.addInstMethodByName(obj,MethodName="remove"+other_class,
                                        sync=True)